# -*- coding: UTF-8 -*-

import xeres

from functor import functor
from attrdict import attrdict

from ui.uxml import component
from ui.uxml import property

__all__ = [ 'Package' ]

class Event(object):
	def __init__(self,func,inst,params):
		self.func = func
		self.inst = inst
		self.params = params
		self.solved = None
	
	def __call__(self, *args):
		args += self.solved
		#print "event call ", args 
		func = self.func
		func( *args )
	
	# Resolve identifier of parameter
	def resolve(self,tree):
		#print "resolving tree " , tree
		resolved = []
		for i in self.params:
			# Resolve parameter
			param = i
			# Mangle names
			seq = param.split('.')
			#print "resolve :" , seq
			context = tree
			if seq[0] == '@self':
				context = self.inst
			for s in xrange(len(seq)-1):
				name = seq[s]
				#print "mangle ", name
				if context == None:
					print "No such context match ", param, "of" ,name
					return
				context = context.get(name)
			if len(seq) > 1:
				context = context[ seq[len(seq)-1] ]
			if isinstance(context,dict):
				context = context['.']
			resolved.append(context)
		self.solved = tuple(resolved)
		#print "all resolved ", self.solved

#
#	Encapsulates a uxml package.
#
class Package(object):
	def __init__(self, comp, code, styles):
		self.comp = comp
		self.code = code
		self.styles = styles
		self.events = []
		self.childtree = None

	# Spawn an instance of package component
	def spawn(self):
		# clear
		self.events = []
		self.childtree = None
		
		# make comp
		id,inst,tree = self.__spawn(self.comp)
		self.childtree = {id:tree}
		tree['.']=inst
		print "tree: " , self.childtree
		
		# complete events
		for i in self.events:
			i.resolve(self.childtree)
		
		#clear	
		self.events = []
		self.childtree = None
		
		return id,inst
		
	# Apply properties
	def __apply(self,comp,instance,childmap):
		# Apply properties.
		for i in comp.properties:
			#print "set property" , i
			value = property.cast_property( instance, i, comp.properties[i], self )
			if value:
				instance.__setattr__( i, value )
		# Apply style property
		if comp.style:
			# first we apply normal style
			default = comp.style.get(None)
			if default:
				detail = instance.style
				Package.__applyStyles(detail,default)
			for pseudo,styles in comp.style.iteritems():
				if pseudo != None:
					try:
						detail = instance.style.__getattribute__(pseudo)
					except:
						print "No such pseudo class [",pseudo,"] in class [",comp.typename,"]."
						continue
				Package.__applyStyles(detail,styles)

	# Spawn non-container component
	def __spawnComp(self, comp):
		# Make new instance of template.
		instance = comp.template()
		return comp.id,instance
	
	@staticmethod
	def __applyStyles(detail,styles):
		# for all styles in normal/pseudo class
		for name,attr in styles.iteritems():
			#try:
			detail.__setattr__(name,attr)
			#except:
			#print "Failed to set style property '%s'." %name

	# Spawn general component
	def __spawn(self, comp):
		childmap = {}
		if isinstance(comp,component.ContainerDef):
			# spawn children
			children = []
			# Make instance of children.
			for i in comp.children:
				childname,child,tree = self.__spawn(i)
				children.append( child )
				if childname:
					if tree:
						childmap[childname] = tree
						tree['.'] = child
					else:
						childmap[childname] = child

			# spawn container
			id,instance = self.__spawnComp(comp)
			# add child
			for child in children:
				instance.AddChild( child )
				# override attribute
				#instance.__setattr__(childname,child)
			self.__apply(comp,instance,childmap)
		else:
			id,instance = self.__spawnComp(comp)
			self.__apply(comp,instance,childmap)

		return id,instance,childmap

	# Make an event action for comp
	def make_event(self, instance, value):
		if self.code:
			# extract call:
			# call looks like:
			#	on_click(@self,form1)
			# it means takes "this" component instance and
			# "form1" sub instance of root component as parameter of event func.
			# which looks like:
			#   def on_click(inst,form1):
			# inst = this component instance & form1 = root->form1
			#
			# all forms of parameter specification:
			# @self  	-  	this instance
			# @param[n]	- 	n-th parameter input
			# [type]	- 	all type instances in current scope as list
			# n.n		- 	n of n's children.
			#
			start = value.find('(')
			end = value.find(')')
			name = value
			param = None
			event = None
			if start != -1 and end != -1:
				name = value[:start]
				param = value[start+1:end].split(',')
				print "params : " , param
				if len(param) == 0:
					param = None
			func = self.code.members.__dict__.get(name)
			if func:
				if param:
					event = Event(func,instance,param)
					self.events.append(event)
					return functor( event )
				else:
					return functor( Package.__event_proxy , func )
			else:
				print "No function (", name, ") has been defined."
				return None
		else:
			print "No codes defined."
			return None

	@staticmethod
	def __event_proxy(func, *args):
		func(*args)
